;;;;;;;;;drawing
;(batch cenarios.clp)
;returns graphical sign for given typeof object
(deffunction getSign (?tipo)
    (if (= (str-compare ?tipo "frog") 0)
        then (return "&"))
    (if (= (str-compare ?tipo "tronco") 0)
        then (return "="))
    (if (= (str-compare ?tipo "tartaruga") 0)
        then (return "^"))
    (if (= (str-compare ?tipo "carro") 0)
        then (return "*"))
    (if (= (str-compare ?tipo "camiao") 0)
        then (return "*"))
    (if (= (str-compare ?tipo "nenufar") 0)
        then (return "@"))
    (if (= (str-compare ?tipo "esquerda") 0)
        then (return "<"))
    (if (= (str-compare ?tipo "direita") 0)
        then (return ">"))
    (if (= (str-compare ?tipo "estrada") 0)
        then (return "-"))
    (if (= (str-compare ?tipo "relva") 0)
        then (return "."))
    (if (= (str-compare ?tipo "rio") 0)
        then (return "~"))
    (return "")
    )

;shoould be fired when still some signs to draw and mobil in position being drawn exists
;responsible for drawing objects (except nenufar) 
(defrule drawSign
    (chosenScenario ?cenario)
    ?f1 <- (drawingColumnIndex ?column)
    (drawingLineIndex ?line)
    (mobil (cenario ?cenario) (tipo ?tipo) (linha ?line) (coluna ?column))
    =>
    (retract ?f1)
    (assert (drawingColumnIndex (+ ?column 1)))
    (printout t (getSign ?tipo))
    )

;responsible for drawing nenufar object
(defrule drawNenufar
    (chosenScenario ?cenario)
    ?f1 <- (drawingColumnIndex ?column)
    (drawingLineIndex ?line)
    (nenufar (cenario ?cenario) (linha ?line) (coluna ?column))
    =>
    (retract ?f1)
    (assert (drawingColumnIndex (+ ?column 1)))
    (printout t (getSign "nenufar"))
    )

;should be fired when  frog is on river
;responsible for drawing frog on river (drawing frog over object)
(defrule drawFrogRiver
    ?f1 <- (drawingColumnIndex ?column)
    (chosenScenario ?cenario)
    (cenario (nome-cenario ?cenario) (min-linha ?minLinha))
    (drawingLineIndex ?line &:(>= ?line ?minLinha))
    (or (linha (cenario ?cenario) (index ?line) (tipo rio)) )
    (or (or (mobil (cenario ?cenario) (tipo tronco) (linha ?line) (coluna ?column)) (mobil (cenario ?cenario)  (tipo tartaruga) (linha ?line) (coluna ?column)))
        (nenufar  (cenario ?cenario) (linha ?line) (coluna ?column)))
    (frog (linha ?line) (coluna ?column))
    =>
    (retract ?f1)
    (assert (drawingColumnIndex (+ ?column 1)))
    (printout t (getSign "frog"))
    
    )

;should be fired when frog is on street
;responsible for drawing frog on street (drawing object over frog if both in the same place)
(defrule drawFrogStreet
    ?f1 <- (drawingColumnIndex ?column)
    (chosenScenario ?cenario)
    (cenario (nome-cenario ?cenario) (min-linha ?minLinha))
    (drawingLineIndex ?line &:(>= ?line ?minLinha))
    (or (linha (cenario ?cenario) (index ?line) (tipo relva))  (linha (cenario ?cenario) (index ?line) (tipo estrada)) )
    (not(mobil (cenario ?cenario) (linha ?line) (coluna ?column)))
    (not(nenufar  (cenario ?cenario) (linha ?line) (coluna ?column)))
    (frog (linha ?line) (coluna ?column))
    =>
    (retract ?f1)
    (assert (drawingColumnIndex (+ ?column 1)))
    (printout t (getSign "frog"))
    
    )

;should be fired when no object in position being currently drawn exists
;responsible for drawing default sign of river, grass or road
(defrule drawDefaultSign 
    ?f1 <- (drawingColumnIndex ?column)
    (chosenScenario ?cenario)
    (cenario (nome-cenario ?cenario) (min-linha ?minLinha))
    (drawingLineIndex ?line &:(>= ?line ?minLinha))
    (not(mobil (cenario ?cenario) (linha ?line) (coluna ?column)))
    (not(nenufar  (cenario ?cenario) (linha ?line) (coluna ?column)))
    (linha (cenario ?cenario) (tipo ?tipo) (index ?line) )
    =>
    (retract ?f1)
    (assert (drawingColumnIndex (+ ?column 1)))
    (printout t (getSign ?tipo))
    )

;responsible to init drawing of next line
(defrule nextLine
    (declare (salience 3))
    (chosenScenario ?cenario)
    (cenario (nome-cenario ?cenario) (max-coluna ?maxColumn) (min-coluna ?minColumn))
    ?f1 <- (drawingColumnIndex ?column &:(> ?column ?maxColumn))
    ?f2 <- (drawingLineIndex ?line)
    (linha (cenario ?cenario) (index ?line) (sentido ?dir) )
    =>
    (retract ?f1)
    (retract ?f2)
    (printout t " "(getSign ?dir) crlf)
    (assert (drawingColumnIndex ?minColumn))
    (assert (drawingLineIndex (- ?line 1)))
    )

;should be fired when allobjects drawn
(defrule end
    (declare (salience -1))
    ?d <- (draw)
    =>
    (printout t crlf)
    (assert(afterDrawing))
    (retract ?d)
    )

;initation of drawing
(defrule initDrawing
    (draw)
    (chosenScenario ?cenario)
    (cenario (nome-cenario ?cenario) (max-linha ?maxLine) (min-coluna ?minColumn))
    =>
    (assert(movedObjectsList))
    (assert(drawingColumnIndex ?minColumn))
    (assert(drawingLineIndex ?maxLine))
    )



;;;;;;;;;;;moving objects;;;;;;;;;
;;;deftemplates

;templete representing fact, that mobil moves from old to new position (column only)
(deftemplate Change
    (slot old)
    (slot new)
    (slot mobil)
    )
;;;rules

;moves single object
;it's important not to modify position of mobils here, because as result we can have two the same object
;for example when we have to objects of the same id, moving one of them to column of second of them will cause 
;that one of them will desappear from memory
(defrule moveObject
    (not(applingMove))
    (chosenScenario ?cenario)
    (cenario (nome-cenario ?cenario) (min-coluna ?minCol) (max-coluna ?maxCol))
    (movingLineIndex ?line)
    (moveObjects ?line ?change)
    ?m <- (mobil (cenario ?cenario) (linha ?line) (coluna ?column))
    ?newPositionsList <- (newPositionsList $?l)
    =>
    (bind ?c (assert(Change(old ?column) (new (changeColumn ?column ?change ?minCol ?maxCol))(mobil ?m))))
    (if (neq ?c FALSE) then
        (retract ?newPositionsList)
        (assert(newPositionsList $?l ?c)) ;attach new change to list of changes to be done in current line
        )
    
    )
;here we move all object in one line manipulating with their ids not to create the same objects
(defrule applyMove
    (declare (salience -1))
    (not(applingMove))
    (movingLineIndex ?line)
    ?newPositions <- (newPositionsList $?list)
    =>
    (bind ?i 0)
    (foreach ?change $?list
        (bind ?mobil2 (fact-slot-value ?change mobil))
        (bind ?old_id (fact-slot-value ?mobil2 id-objecto))
        (bind ?new_id (str-cat ?old_id ?i))
        (modify ?mobil2 (coluna (fact-slot-value ?change new)) (id-objecto  ?new_id) )
        (++ ?i)
        (if (> ?i 9) then (bind ?i 0))
        )
    (foreach ?change $?list
        (bind ?mobil (fact-slot-value ?change mobil))
        (bind ?new_id (fact-slot-value ?mobil2 id-objecto))
        (bind ?old_id (sub-string 1  (- (str-length ?new_id) 1)  ?new_id))
        (modify ?mobil (coluna (fact-slot-value ?change new)) (id-objecto  ?old_id))
        (retract ?change)
        )
    (retract ?newPositions)
    (assert(newPositionsList))
    (assert(applingMove))
    )

;reposible for initiation of moving next line
(defrule moveLine
    (not(applingMove))
    (chosenScenario ?cenario)
    (movingLineIndex ?line)
    (stepCounter ?step)
    (linha (cenario ?cenario) (index ?line) (sentido ?dir &: (neq ?dir nil)) (velocidade ?speed ))
    ?m <- (moveObjects ?l &: (<> ?l ?line ) ?c ) ;if ?l == ?line means that it was already moved
    =>
    (retract ?m)
    (assert(moveObjects ?line (getChange ?dir ?speed ?step)))
    )

;responsible for ending moving lines, because all objects were already moved
(defrule noMoreObjectToMoveInLine
    (declare (salience -2))
    ?applyingMove <- (applingMove)
    (moveObjects)
    ?f <- (movingLineIndex ?line &: (> ?line -8))
    ?newPositions <- (newPositionsList)
    =>
    (retract ?applyingMove)
    (retract ?f)
    (assert(movingLineIndex (- ?line 1)))
    )

;responsible for not moving line if there is nothing to move (when line doesn't have speed)
(defrule skipLine
    (not(applingMove))
    ?f <- (movingLineIndex ?line)
    (chosenScenario ?cenario)
    (linha (cenario ?cenario) (index ?line) (sentido nil))
    =>
    (retract ?f)
    (assert(movingLineIndex (- ?line 1)))
    )


(defrule skipLine2
    ?f <- (movingLineIndex ?line)
    (chosenScenario ?cenario)
    (linha (cenario ?cenario) (index ?line) (sentido nil))
    ?applyingMove <- (applingMove)
    =>
    (retract ?f)
    (assert(movingLineIndex (- ?line 1)))
    (retract ?applyingMove)
    )

;function returns the change of position of object according to current step
;we assume, that if velocity=v means how slow is object, we move object every v steps using modulo calculation
(deffunction getChange (?direction ?speed ?currentStep)
    (if (= (mod ?currentStep ?speed) 0) then
        (if (= (str-compare ?direction "esquerda") 0)
            then
            (return -1)
            )
        (if (= (str-compare ?direction "direita") 0)
            then
            (return 1)
            )
        )
    (return 0)
    )

;moves frog with object, that it stays on
(deffunction moveFrogWithMobil(?f ?tipo ?sentido ?velocity ?step ?minCol ?maxCol)
    (if (neq ?velocity nil) then
        (if (= (str-compare ?tipo "rio") 0) then
            (if (= (mod ?step ?velocity) 0) then
                (if (= (str-compare ?sentido "esquerda") 0) then
                    (moveFrogLeft ?f ?minCol ?maxCol)
                    else
                    (moveFrogRight ?f ?minCol ?maxCol)
                    )
                )
            )
        )
    )

;change column value, not allowing new column to be out of board
(deffunction changeColumn (?column ?change ?minCol ?maxCol)
    (bind ?newCol (+ ?column ?change))
    (if (< ?newCol ?minCol) then
        (return ?maxCol)
        )
    (if (> ?newCol ?maxCol) then
        (return ?minCol))
    (return ?newCol)
    )

;initiates moving objects on board
(defrule initMovingObjects
    (moveObjects)
    (chosenScenario ?cenario)
    (cenario (nome-cenario ?cenario) (max-linha ?maxLine))
    =>
    (assert(movingLineIndex ?maxLine))
    (assert (moveObjects (random) (random) ))
    (assert (newPositionsList))
    )

;end moving objects
(defrule endMoving
    (declare (salience -4))
    (chosenScenario ?cenario)
    ?f <- (frog  (linha ?line) (coluna ?col))
    (linha (cenario ?cenario) (tipo ?tipo) (index ?line) (sentido ?sentido) (velocidade ?velocity))
    ?m <- (moveObjects)
    (stepCounter ?step)
    (cenario (nome-cenario ?cenario) (min-coluna ?minCol) (max-coluna ?maxCol))
    =>
    (retract ?m)
    (moveFrogWithMobil ?f ?tipo ?sentido ?velocity ?step ?minCol ?maxCol)
    (assert(afterMovingObjects))
    )


;;;;;;;Moving frog;;;;
;moves frog in given direction with one step
(deffunction moveFrogDirection (?f ?direction ?minCol ?maxCol)
    (if  (= (str-compare ?direction "esquerda") 0) then
        (moveFrogLeft ?f ?minCol ?maxCol)
        )
    (if (= (str-compare ?direction "direita") 0) then
        (moveFrogRight ?f ?minCol ?maxCol)
        )
    )

;moves frog in opposite direction that given with one step
(deffunction moveFrogOppositeDirection (?f ?direction ?minCol ?maxCol)
    (if  (= (str-compare ?direction "esquerda") 0) then
        (moveFrogRight ?f ?minCol ?maxCol)
        )
    (if (= (str-compare ?direction "direita") 0) then
        (moveFrogLeft ?f ?minCol ?maxCol)
        )
    )

;moves frog one line up
(deffunction moveFrogUp (?f)
    (bind ?frogLine (fact-slot-value ?f linha))
    (bind ?frogNewLine (+ ?frogLine 1))
    (modify ?f (linha ?frogNewLine)))

;moves frog one column left (checking not to go out of board)
(deffunction moveFrogLeft (?f ?minCol ?maxCol)
    (bind ?frogColumn (fact-slot-value ?f coluna))
    (bind ?frogNewColumn (- ?frogColumn 1))
    (if (< ?frogNewColumn ?minCol)
        then
        (modify ?f (coluna ?maxCol))
        else (modify ?f (coluna ?frogNewColumn)))
    )


;moves frog one column right (checking not to go out of board)
(deffunction moveFrogRight (?f ?minCol ?maxCol)
    (bind ?frogColumn (fact-slot-value ?f coluna))
    (bind ?frogNewColumn (+ ?frogColumn 1))
    (if (> ?frogNewColumn ?maxCol)
        then
        (modify ?f (coluna ?minCol))
        else
        (modify ?f (coluna ?frogNewColumn)))
    )

;returns opposite direction to given
(deffunction oppositeDirection(?direction)
    
    (if  (= (str-compare ?direction "esquerda") 0) then
        (return "direita")
        )
    (if (= (str-compare ?direction "direita") 0) then
        (return "esquerda")
        )
    )

;checks if given column endeanger frog (it's one more or less than column of frog, so object 
;being in this column can kill frog in next move)
(deffunction columnEndangerFrog (?col ?direction ?MobilCol)
    
    (if  (= (str-compare ?direction "esquerda") 0) then
        (return (= (+ ?col 1) ?MobilCol))
        )
    (if (= (str-compare ?direction "direita") 0) then
        (return (= (- ?col 1) ?MobilCol))
        )
    )

;checks if given column near frog is occupied by given mobil column
(deffunction columnOccupiedByMobil (?col ?direction ?MobilCol)
    
    (if  (= (str-compare ?direction "esquerda") 0) then
        (return (= (- ?col 1) ?MobilCol))
        )
    (if (= (str-compare ?direction "direita") 0) then
        (return (= (+ ?col 1) ?MobilCol))
        )
    )

;;
;;function returns TRUE if mobil doesn't move next step and FALSE if mobil does move next step
;;
(deffunction wehicleDoesntMoveNextStep (?velocity ?currentStep) ;for street only
    (bind ?nextStep (+ ?currentStep 1))
    (if (= (mod ?nextStep ?velocity) 0)  ;then mobil make step in next move
        then
        (return FALSE)
        )
    (return TRUE)
    )

; moving on grass
;if next line is grass we can move frog up
(defrule onGrass
    (chosenScenario ?cenario)
    ?m <- (moveFrog)
    ?f <- (frog (linha ?line) (coluna ?col)  (killed FALSE))
    (linha (cenario ?cenario) (tipo relva) (index ?l &: (= (+ ?line 1) ?l)))
    =>
    (moveFrogUp ?f)
    (retract ?m)
    (assert(afterFrogMove))
    )
;
;moving on river
;
;checks if next line in the column of frog is occupied by object, so we can move frog there
(defrule ifUpperPositionFriendAndWeAreOnRiver
    (declare (salience 3)) ;most valuable move is up
    (chosenScenario ?cenario)
    ?m <- (moveFrog )
    ?f <- (frog (linha ?line) (coluna ?col) (killed FALSE))
    (linha (cenario ?cenario) (tipo rio) (index ?l &: (= (+ ?line 1) ?l)) (sentido ?sentido) (velocidade ?velocity))
    (or (mobil (cenario ?cenario) (linha ?l ) (coluna ?col)) (nenufar (cenario ?cenario) (linha ?l) (coluna ?col)))
    =>
    ;(printout t "up is wood" crlf)
    (moveFrogUp ?f)
    (retract ?m)
    (assert(afterFrogMove))
    )

;if moving up is not possible we try to move frog in direction of river in current line
;checks if  there is object to move on
(defrule upperNotPossibleMoveDirectionOfRiver
    (declare (salience 2)) ;better to move in direction of river, because wood can move also, so in the worst situation we won't move at all
    (chosenScenario ?cenario)
    ?m <- (moveFrog )
    ?f <- (frog (linha ?line) (coluna ?col) (killed FALSE))
    (linha (cenario ?cenario) (tipo rio) (index ?l &: (= (+ ?line 1) ?l)))
    (linha (cenario ?cenario) (tipo rio) (index ?line ) (sentido ?direction) (velocidade ?velocity))
    (mobil (cenario ?cenario) (linha ?line) (coluna ?MobilCol &: (columnOccupiedByMobil ?col ?direction ?MobilCol)))
    (cenario (nome-cenario ?cenario) (min-coluna ?minCol) (max-coluna ?maxCol))
    =>
    ;(printout t "we can move with direction of river" crlf)
    (if (neq ?direction nil) then
        (moveFrogDirection ?f ?direction ?minCol ?maxCol)
        )
    (retract ?m)
    (assert(afterFrogMove))
    )

;we try to move frog in opposite direction to direction river in current line
;checks if  there is object to move on
(defrule upperNotPossibleMoveDirectionNotPossibleMoveOtherDirectionOfRiver
    (declare (salience 1)) ;better to move in direction of river, because wood can move also, so in the worst situation we won't move at all
    (chosenScenario ?cenario)
    ?m <- (moveFrog )
    ?f <- (frog (linha ?line) (coluna ?col) (killed FALSE))
    (linha (cenario ?cenario) (tipo rio) (index ?l &: (= (+ ?line 1) ?l)))
    (linha (cenario ?cenario) (tipo rio) (index ?line ) (sentido ?direction) (velocidade ?velocity))
    (mobil (cenario ?cenario) (linha ?line) (coluna ?MobilCol &: (columnOccupiedByMobil ?col (oppositeDirection ?direction) ?MobilCol)))
    (cenario (nome-cenario ?cenario) (min-coluna ?minCol) (max-coluna ?maxCol))
    =>
   ; (printout t "we can move with opposite direction to direction of river" crlf)
    (if (neq ?direction nil) then
        (moveFrogOppositeDirection ?f ?direction ?minCol ?maxCol)
        )
    (retract ?m)
    (assert(afterFrogMove))
    )
;
;moving on road
;

;tries to move frog up
;move is possible if there is no mobil currently on that position and
;in next step there won't be any mobil in that possition
(defrule ifUpperPositionFreeAndWeAreOnRoad ;it matches if there is no mobil on left/right (depending on sentido) or there is mobil but doesn't move net step
    (declare (salience 3)) ;most valuable move is up
    (chosenScenario ?cenario)
    ?m <- (moveFrog )
    ?f <- (frog (linha ?line) (coluna ?col)(killed FALSE))
    (linha (cenario ?cenario) (tipo estrada) (index ?l &: (= (+ ?line 1) ?l)) (sentido ?direction) (velocidade ?velocity))
    (not(mobil (cenario ?cenario) (linha ?l ) (coluna ?col))) ;this line check if there is no mobile over
    (stepCounter ?currentStep)
    (or (and
            (mobil (cenario ?cenario) (linha ?l) (coluna ?MobilCol &: (columnEndangerFrog ?col ?direction ?MobilCol)))
            (test (wehicleDoesntMoveNextStep ?velocity ?currentStep)))
        (not (mobil (cenario ?cenario) (linha ?l) (coluna ?MobilCol &: (columnEndangerFrog ?col ?direction ?MobilCol))))
        )
    =>
    ;(printout t "up is free" crlf)
    ;(printout t "and no danger in next step" crlf)
    (moveFrogUp ?f)
    (retract ?m)
    (assert(afterFrogMove))
    )

;tries to move frog in direction of road because, that move is more safe (we won't crash to another vehicle)
;checks if there is no object in position to which we want to move frog
(defrule upImpossibleMoveWithDirectionOfRoad
    (declare (salience 2)) ;less valuable then moving up
    (chosenScenario ?cenario)
    ?m <- (moveFrog)
    ?f <- (frog (linha ?line) (coluna ?col) (killed FALSE))
    (linha (cenario ?cenario) (tipo estrada) (index ?l &: (= (+ ?line 1) ?l)))
    (linha (cenario ?cenario) (tipo ?tipoFrogLine) (index ?line)  (sentido ?direction) (velocidade ?velocity))
    (not(mobil (cenario ?cenario) (linha ?line) (coluna ?MobilCol &: (columnOccupiedByMobil ?col ?direction ?MobilCol))))
    (cenario (nome-cenario ?cenario) (min-coluna ?minCol) (max-coluna ?maxCol))
    =>
    ;(printout t "moving in direction of road" crlf)
    (if (neq ?direction nil) then
        (moveFrogDirection ?f ?direction ?minCol ?maxCol)
        )
    
    (retract ?m)
    (assert(afterFrogMove))
    )

;tries to move frog in opposite direction to direction of river in current line
;checks if there is no object in position to which we want to move frog
(defrule upImpossibleAndWithDirectionOfRoadImpossible
    (declare (salience 1)) ;less valuable then moving up
    (chosenScenario ?cenario)
    ?m <- (moveFrog)
    ?f <- (frog (linha ?line) (coluna ?col) (killed FALSE))
    (linha (cenario ?cenario) (tipo estrada) (index ?l &: (= (+ ?line 1) ?l)))
    (linha (cenario ?cenario) (tipo ?tipoFrogLine) (index ?line)  (sentido ?direction) (velocidade ?velocity))
    (mobil (cenario ?cenario) (linha ?line) (coluna ?MobilCol &: (columnOccupiedByMobil ?col ?direction ?MobilCol)))
    (cenario (nome-cenario ?cenario) (min-coluna ?minCol) (max-coluna ?maxCol))
    =>
    ;(printout t "moving in opposite direction of road" crlf)
    (if (neq ?direction nil) then
        (moveFrogOppositeDirection ?f ?direction ?minCol ?maxCol)
        )
    (retract ?m)
    (assert(afterFrogMove))
    )

;default move is to stay in the same place
(defrule  default 
    ?m <- (moveFrog)
    =>
    (retract ?m)
    (assert(afterFrogMove))
    )







